package com.zhuang.springbootredis.lock;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

import java.util.*;
import java.util.concurrent.TimeUnit;

public class RedLock implements AutoCloseable {
    private final List<Jedis> jedisClients;
    private final String lockKey;
    private final String lockValue;
    private final int expireTime; // 锁的过期时间（秒）
    private final int quorum; // 法定人数

    public RedLock(List<Jedis> jedisClients, String lockKey, int expireTime) {
        this.jedisClients = jedisClients;
        this.lockKey = lockKey;
        this.expireTime = expireTime;
        this.lockValue = UUID.randomUUID().toString();
        this.quorum = (jedisClients.size() / 2) + 1;
    }

    /**
     * 获取RedLock锁
     * @param timeout 超时时间（毫秒）
     * @return 是否获取成功
     */
    public boolean acquire(long timeout) {
        long startTime = System.currentTimeMillis();
        long endTime = startTime + timeout;
        List<Jedis> acquiredClients = new ArrayList<>();

        while (System.currentTimeMillis() < endTime) {
            // 尝试在所有节点获取锁
            for (Jedis client : jedisClients) {
                try {
                    //String result = client.set(lockKey, lockValue, "NX", "EX", expireTime);
                    String result = client.set(lockKey, lockValue, SetParams.setParams().nx().ex(expireTime));
                    if ("OK".equals(result)) {
                        acquiredClients.add(client);
                    }
                } catch (Exception e) {
                    // 忽略异常，继续尝试其他节点
                }
            }

            // 检查是否达到法定人数
            if (acquiredClients.size() >= quorum) {
                return true;
            }

            // 未达到法定人数，释放已获取的锁
            releaseLocks(acquiredClients);
            acquiredClients.clear();

            // 重试前等待一段时间
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }

        return false;
    }

    /**
     * 释放RedLock锁
     */
    public void release() {
        releaseLocks(jedisClients);
    }

    private void releaseLocks(List<Jedis> clients) {
        String script =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "   return redis.call('del', KEYS[1]) " +
                        "else " +
                        "   return 0 " +
                        "end";

        for (Jedis client : clients) {
            try {
                client.eval(script, 1, lockKey, lockValue);
            } catch (Exception e) {
                // 忽略释放失败的异常
            }
        }
    }

    @Override
    public void close() {
        release();
    }
}
